﻿using System;
using YasharEl.Infrastructure.ApplicationModel.Modularity.Bootstrap;
using YasharEl.Infrastructure.ApplicationModel.Bootstrap.Configuration;
using YasharEl.Infrastructure.EntityModel.Configuration;
using System.Configuration;
using YasharEl.Infrastructure.EntityModel.Validation;
using YasharEl.Infrastructure.ObjectFactory;
using YasharEl.Infrastructure.EntityModel.Specifications;
using YasharEl.Infrastructure.EntityModel;

namespace YasharEl.Infrastructure.ApplicationModel.Bootstrap.Extensions
{
    public class EntitiesModelServicesConfigurationBootstrapperExtension : ModuleComponentBootstrappingExtension, IConsumeConfigurationSection
    {
        #region Properties

        public EntityModelConfig EntityModelConfigSection { get; private set; }

        #endregion

        #region IConsumeConfigurationSection Members

        public void Apply(ConfigurationSection section)
        {
            EntityModelConfigSection = section as EntityModelConfig;
        }

        #endregion

        #region ModuleComponentBootstrappingExtension Overrides

        public override string SectionName
        {
            get { return EntityModelConfig.EntityModelConfigDefaultSectionName; }
        }

        #endregion

        #region BaseComplexBootstrappingExtension Describe

        public override string Describe()
        {
            return "Extension which configure and start entities model services(validations, specifications, unit of works, repositories etc).";
        }

        #endregion

        #region BaseComplexBootstrappingExtension Configure

        public override void Configure()
        {
            base.Configure();

            if (EntityModelConfigSection == null)
            {
                Logger.Warn("There is no entity model services to configure.");
                return;
            }

            ConfigureDateTimeProvider(EntityModelConfigSection.DateTimeProvider);
            ConfigureEntityValidation(EntityModelConfigSection.EntityValidation);
            ConfigureEntitySpecifications(EntityModelConfigSection.EntitySpecifications);
            ConfigureUnitOfWork(EntityModelConfigSection.UnitOfWork);
        }

        #endregion

        #region Private Methods

        private void ConfigureDateTimeProvider(DateTimeProviderConfig dateTimeProviderConfig)
        {
            if (dateTimeProviderConfig != null)
            {
                Container.DependencyRegistrar.Register<IDateTimeProvider>(
                    new DateTimeProvider(dateTimeProviderConfig.UseUtc, dateTimeProviderConfig.StandartFormat),
                    true
                );
            }
            else
            {
                Container.DependencyRegistrar.Register<IDateTimeProvider>(
                    new DateTimeProvider(true, "MM/dd/yyyy hh:mm:ss"), true
                );
            }
        }

        private void ConfigureEntityValidation(EntityValidationConfig entityValidationConfig)
        {
            // Register entity validation factory as singleton
            Container.DependencyRegistrar.Register(
                typeof(IEntityValidatorFactory),
                entityValidationConfig.EntityValidatorFactoryType,
                true
            );

            // Register entity validator resolving to factory method.
            FactoryMethod factoryMethod = new FactoryMethod(
                    (genericType, resolver, requestedType, key) => resolver.Resolve<IEntityValidatorFactory>().Create()
            );

            // For each Resolve entity validator factory method will create  
            // new transient instance of entity validator.
            Container.DependencyRegistrar.RegisterFactory(typeof(IEntityValidator), factoryMethod);
        }

        private void ConfigureEntitySpecifications(EntitySpecificationsConfig entitySpecificationsConfig)
        {
            // Register entity specification locator as singleton
            Container.DependencyRegistrar.Register(
                typeof(ISpecificationLocator),
                entitySpecificationsConfig.SpecificationLocatorType,
                true
            );

            if (entitySpecificationsConfig.Specifications != null && entitySpecificationsConfig.Specifications.Count > 0)
            {
                foreach (EntitySpecificationConfig entitySpecificationConfig in entitySpecificationsConfig.Specifications)
                {
                    // Registering entity specification to container as transient resolution.
                    Container.DependencyRegistrar.Register(
                        entitySpecificationConfig.InterfaceType, // from type
                        entitySpecificationConfig.SpecificationType, // to type
                        false
                    );
                }
            }
        }

        private void ConfigureUnitOfWork(UnitOfWorkConfig unitOfWorkConfig)
        {
            if (unitOfWorkConfig != null && unitOfWorkConfig.ElementInformation.IsPresent)
            {
                // Register unit of work convertor
                Container.DependencyRegistrar.Register(
                    typeof(IUnitOfWorkConvertor),
                    unitOfWorkConfig.UnitOfWorkConvertorType,
                    true
                );

                // Register persistence model configuration builder used in unit of workf factoring
                Container.DependencyRegistrar.Register(
                    unitOfWorkConfig.PersistenceModelConfigurationBuilder.PersistenceModelConfigurationBuilderInterfaceType,
                    unitOfWorkConfig.PersistenceModelConfigurationBuilder.PersistenceModelConfigurationBuilderImplementationType,
                    true
                );


                // Register entity validation factory as singleton
                Container.DependencyRegistrar.Register(
                    typeof(IUnitOfWorkFactory),
                    unitOfWorkConfig.UnitOfWorkFactoryType,
                    true,
                    new object[] 
                    { 
                        Container.DependencyResolver,
                        unitOfWorkConfig.PersistenceModelConfigurationBuilder.ConnectionStringName, 
                        unitOfWorkConfig.PersistenceModelConfigurationBuilder.SettingsFile 
                    }
                );

                Logger.Debug("UnitOfWorkFactoryType registered as {0}...", unitOfWorkConfig.UnitOfWorkFactoryType);

                FactoryMethod factoryMethod = new FactoryMethod(
                        (genericType, resolver, requestedType, key) => resolver.Resolve<IUnitOfWorkFactory>().BeginUnitOfWork()
                );

                // Register unit of work factory method to container.
                // For each Resolve unitOfWork factory method will create 
                // new per execution scope(like aspnet request,wcf session, etc unit of work scope) unit of work.
                Container.DependencyRegistrar.RegisterFactory(typeof(IUnitOfWork), factoryMethod, DependencyLifeStyle.ModularUnitOfWork, ModuleName);

                if (unitOfWorkConfig.Repositories != null && unitOfWorkConfig.Repositories.Count > 0)
                {
                    foreach (RepositoryConfig repositoryConfig in unitOfWorkConfig.Repositories)
                    {
                        Container.DependencyRegistrar.Register(
                            repositoryConfig.RepositoryInterfaceType,
                            repositoryConfig.RepositoryImplementationType,
                            false
                        );

                        Logger.Debug("Repository with type '{0}' registered and implemented in type '{1}'",
                            repositoryConfig.RepositoryInterfaceType.FullNameToString(),
                            repositoryConfig.RepositoryImplementationType.FullNameToString()
                        );
                    }
                }
            }
            else
            {
                Logger.Debug("Unit of work factory not configured and not registered");
            }
        }

        #endregion
    }
}
